package com.ldl.handle.spring.framework.beans.support;

import com.ldl.handle.spring.framework.beans.config.LdlBeanDefinition;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * @author ldl  想过夏天的雪人
 * @Date 2022-10-13 11:31
 */
public class LdlBeanDefinitionReader {
    //保存用户配置好的配置文件
    private Properties contextConfig = new Properties();

    //缓存从包路径下扫描的全类名 被注册的bean classname
    private List<String> registryBeanClasses = new ArrayList<String>();

    public LdlBeanDefinitionReader(String... locations) {
        //1. 加载properties文件
        doLoadConfig(locations[0]);
        doScanner(contextConfig.getProperty("scanPackage"));

    }

    public List<LdlBeanDefinition> loadBeanDefinitions() {
        List<LdlBeanDefinition> result = new ArrayList<LdlBeanDefinition>();
        try {
            for (String className : registryBeanClasses) {

                Class<?> beanClass = Class.forName(className);
                //bnean class 本身是接口 不处理
                if (beanClass.isInterface()) {
                    continue;
                }
                //如果不是接口 就按照类名作为key
                result.add(doCreateBeanDefinition(toLowerFirstCase(beanClass.getSimpleName()), beanClass.getName()));
                //如果是接口，使用实现类  //接口 补充实现类 key 接口全路径，value 实现类全路径  暂不支持多实现
                for (Class<?> i : beanClass.getInterfaces()) {
                    result.add(doCreateBeanDefinition(i.getName(), beanClass.getName()));
                }

            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return result;
    }

    private LdlBeanDefinition doCreateBeanDefinition(String factoryBeanName, String factoryClassName) {
        LdlBeanDefinition definition = new LdlBeanDefinition();
        definition.setFactoryBeanName(factoryBeanName);
        definition.setBeanClassName(factoryClassName);
        return definition;
    }


    //根据contextConfigLocation的路径去ClassPath下找到对应的配置文件  web.xml init-param
    private void doLoadConfig(String contextConfigLocation) {

        InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation.replaceAll("classpath:",""));
        try {
            contextConfig.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //扫描ClassPath下符合包路径规则所有的Class文件
    private void doScanner(String scanPackage) {

        URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
        File classPath = new File(url.getFile());

        for (File file : classPath.listFiles()) {
            if (file.isDirectory()) {
                doScanner(scanPackage + "." + file.getName());
            } else {
                //取反，减少代码嵌套
                if (!file.getName().endsWith(".class")) {
                    continue;
                }

                //包名.类名  比如： com.demo
                String className = (scanPackage + "." + file.getName().replace(".class", ""));
                //实例化，要用到  Class.forName(className);
                registryBeanClasses.add(className);
            }

        }

    }

    private String toLowerFirstCase(String simpleName) {
        char[] chars = simpleName.toCharArray();
        chars[0] += 32;     //利用了ASCII码，大写字母和小写相差32这个规律
        return String.valueOf(chars);
    }

}
